package com.wwdx.controller.rest;

import com.wwdx.controller.base.BaseController;
import com.wwdx.model.WwdxOrderModel;
import com.wwdx.model.WwdxPersonalModel;
import com.wwdx.model.dto.BasePayDTO;
import com.wwdx.model.dto.ResultBean;
import com.wwdx.utils.ConstantHolder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;

/**
 * @author liguoliang
 * @date 2017/12/5 9:16
 */
@RestController
@RequestMapping("alipay")
@Api(tags = "APP支付宝支付接口")
public class RestAliPayController extends BaseController{

    /**
     * 支付宝支付
     */
    @RequestMapping("/alisave")
    @ApiOperation(value = "支付宝支付",httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "token",value = "令牌",required = true),
            @ApiImplicitParam(name = "sign",value = "签名",required = true),
            @ApiImplicitParam(name = "type",value = "产品类型",required = true),
            @ApiImplicitParam(name = "product_id",value = "产品ID",required = true),
            @ApiImplicitParam(name = "channel",value = "渠道",required = true)
    })
    public ResultBean alisave(String token, String sign, Integer type,String channel,
                              @RequestParam(value = "product_id") Long productId) throws UnsupportedEncodingException{

        SortedMap<String, Object> sortedMap = super.getSortedMap(token,sign);
        sortedMap.put("product_id", productId);
        ResultBean resultBean = super.validSignTokenResult(sortedMap);
        if (resultBean.getStatus() == 1){
            return resultBean;
        }
        WwdxOrderModel model = WwdxOrderModel.builder().payType(ConstantHolder.Constant.ALI_APP_PAYTYPE).type(type)
                .product_id(productId).channel(channel).build();
        logger.info("支付宝支付:{}",model);
        String unionId = (String) resultBean.getData();
        model.setUnionid(unionId);
        BasePayDTO.PayModel payModel = payService.save(model, super.makeOrderNum());
        if (payModel.getError() != null) {
            return renderError(payModel.getError());
        }
        WwdxPersonalModel wwdxPersonalModel = wwdxPersonalService.getByUnionid(unionId);
        String totalFee = "0.01";
        Integer white = wwdxPersonalModel.getWhite();
        if (white == null || white == 0){
            totalFee = payModel.getProductPrice().toString();
        }

        //生成订单
        sortedMap.clear();
        //通知接口
        sortedMap.put("service", aliPayConfig.getService());
        //合作者身份ID
        sortedMap.put("partner", aliPayConfig.getPartner());
        //字符集
        sortedMap.put("_input_charset", "UTF-8");
        //卖家id
        sortedMap.put("seller_id", aliPayConfig.getSellerId());
        //生成的订单号
        sortedMap.put("out_trade_no", payModel.getOrderno());
        //商品名称
        sortedMap.put("subject", payModel.getProductName());
        //总金额
        sortedMap.put("total_fee", totalFee);
        //回调地址
        sortedMap.put("notify_url", aliPayConfig.getNotifyUrl());
        //支付类型 1商品购买
        sortedMap.put("payment_type", "1");
        StringBuilder sb = new StringBuilder();
        sortedMap.forEach((k,v)-> sb.append(k).append("=").append("\"").append(v).append("\"").append("&"));
        String data = sb.substring(0, sb.length() - 1);
        String rsaSign = this.sign(data, aliPayConfig.getPrivateKey());
        rsaSign = URLEncoder.encode(rsaSign == null ? "" : rsaSign, "UTF-8");
        data = data + "&sign=" + "\"" + rsaSign + "\"" + "&sign_type=" + "\"" + aliPayConfig.getSignType() + "\"";
        return renderSuccess(data);
    }

    /**
     * 支付宝回调通知
     */
    @RequestMapping("/alipaycallback")
    @ApiIgnore
    public synchronized String alipaycallback(HttpServletRequest request) {
        Map<String, String> params = this.getParameterMap(request);
        String notifyId = params.get("notify_id"),tradeStatus = params.get("trade_status"),
                result = "true",status = "TRADE_SUCCESS";
        if (verifyResponse(notifyId).equals(result) && tradeStatus.equals(status)) {
            //回调处理
            BasePayDTO.PayCallBackDTO dto = new BasePayDTO.PayCallBackDTO();
            //商户订单号
            String outTradeNo = params.get("out_trade_no");
            dto.setTradeNo(outTradeNo);
            taskExecutor.execute(()-> payService.payBackHandler(dto));
            logger.info("支付宝回调结果：{}",params.get("trade_status"));
            return "SUCCESS";
        }
        return "FAIL";
    }

    /**
     * 获取远程服务器ATN结果,验证返回URL
     *
     * @param notifyId
     *            通知校验ID
     * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true
     *         返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    private String verifyResponse(String notifyId) {
        //支付宝消息验证地址
        String validateUrl = "https://mapi.alipay.com/gateway.do?service=notify_verify&";
        // 获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求
        String veryfyUrl = validateUrl + "partner=" + aliPayConfig.getPartner() + "&notify_id=" + notifyId;
        return checkUrl(veryfyUrl);
    }

    /**
     * 获取远程服务器ATN结果
     *
     * @param urlvalue
     *            指定URL路径地址
     * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true
     *         返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    private String checkUrl(String urlvalue) {
        String inputLine;
        HttpURLConnection urlConnection = null;
        BufferedReader in = null;
        try {
            URL url = new URL(urlvalue);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            inputLine = in.readLine();
            return inputLine;
        } catch (IOException e) {
            logger.error(e.getMessage());
            return null;
        }finally {
            try {
                if (in != null){
                    in.close();
                }
                if (urlConnection != null){
                    urlConnection.disconnect();
                }
            }catch (IOException e){
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * RSA签名
     * @param content 待签名数据
     * @param privateKey 商户私钥
     * @return 签名值
     */
    private String sign(String content, String privateKey) {
        try {
            PKCS8EncodedKeySpec priPKCS8 	= new PKCS8EncodedKeySpec(ConstantHolder.DECODER.decode(privateKey));
            KeyFactory keyf 				= KeyFactory.getInstance("RSA");
            PrivateKey priKey 				= keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
            signature.initSign(priKey);
            signature.update(content.getBytes(ConstantHolder.Constant.UTF8));
            byte[] signed = signature.sign();
            return ConstantHolder.ENCODER.encodeToString(signed);
        } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException
                | UnsupportedEncodingException | SignatureException e) {
            logger.error("错误信息：{}",e.getMessage());
            return null;
        }
    }

    /**
     * 请求中参数转Map<String, String>,for支付宝异步回调,
     * 平时建议直接使用request.getParameterMap(),返回Map<String, String[]>
     * @param request HttpServletRequest
     * @return map
     */
    private Map<String, String> getParameterMap(HttpServletRequest request) {
        Map<String, String> result = new HashMap<>(16);
        Enumeration parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parameterName = (String) parameterNames.nextElement();
            result.put(parameterName, request.getParameter(parameterName));
        }
        return result;
    }
}